package com.wizo.csb.records;

import java.io.IOException;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Properties;

import com.wizo.csb.records.exporter.*;
import com.wizo.csb.Assist;

public class Records {
	public static final String RECORD_TYPE = "recordType";
	public static final String RECORD_TYPE_DEFAULT = "com.wizo.csb.records.OneRecordTimeSeries";
	static String _type; 
	
	HashMap<String, OneRecord> _records;
	Properties _props;
	long _fileSize = 0;
	long _operations = 0;
	long _latencies = 0;
	long _start = -1;
	boolean getBW = false;
	
	public Records(Properties props){
		_records = new HashMap<String, OneRecord>();
		_props = props;
		_type = _props.getProperty(RECORD_TYPE, RECORD_TYPE_DEFAULT);
		_fileSize = Assist.getObjectSizeFromString(_props.getProperty("objectSize"));
	}
	
	public void setStart(long start){
		_start = start;
	}
	
	OneRecord constructOneRecord(String name){
		OneRecord one = null;
		
		try{
			Class parTypes[] = new Class[2];
			parTypes[0] = String.class;
			parTypes[1] = Properties.class;
			Object argList[] = new Object[2];
			argList[0] = name;
			argList[1] = _props;
			
			one = (OneRecord)Class.forName(_type).getConstructor(parTypes).newInstance(argList);
		}catch(Exception e){
			System.err.println("Could not find recordType " + _type
					+ ", will use default text reporter.");
			e.printStackTrace();
			one = new OneRecordTimeSeries(name, _props);
		}
		return one;
		
	}
	
	/**
	 * Report a single value of a single metric. 
	 * @param operation Operation name. E.g. for read latency is "READ"
	 * @param latency Latency(ms)
	 */
	public void record(String operation, long latency){
		if(!_records.containsKey(operation))
			_records.put(operation, constructOneRecord(operation));
		
		_records.get(operation).record(latency);
		_operations++;
		_latencies += latency;
	}
	
	public void getOperationInfo(){
		DecimalFormat format = new DecimalFormat("#,###.##");
		System.out.println("operations: "+_operations+" Average Latency(ms) " +
				"per operations: "+format.format(((double)_latencies)/((double)_operations)));
	}
	
	public void reportLatencies(RecordsExporter exporter) throws IOException{
		for(OneRecord oneRecord:_records.values()){
			oneRecord.reportLatencies(exporter);
		}
	}
	
	public void reportRecords(RecordsExporter exporter, long now)
			throws IOException{
		DecimalFormat format = new DecimalFormat("#,###.##");
		boolean getBandwidth = true;
		
		checkEndOfUnit(now);

		for(OneRecord measurement:_records.values()){
			if(getBandwidth&&(!measurement.getName().equals("READ"))
					&&(!measurement.getName().equals("WRITE")))
				getBandwidth = false;
			
			measurement.reportOneRecord(exporter);
		}
		
		exporter.write("");
		exporter.write("Operations Count", format.format(_operations));
		
		if(getBandwidth){
			exporter.write("Average Bandwidth(KB/s)", format.format(getBandwidth()));
		}
		
		exporter.write("Average Latency(ms) Per Operation", format.format(getAverageLatencyPO()));
		exporter.write("Average Operations Per Second", format.format(getAverageOperationsPS()));

		exporter.write("-------------------------------------------");
		exporter.write("");

		getBW = getBandwidth;
	}
	
	//Used for OneRecordTimeSeries to record timeSeries
	public void checkEndOfUnit(long now){
		if(_start<0){
			_start = now;
		}
		
		for(OneRecord record:_records.values()){
			OneRecordTimeSeries omts = (OneRecordTimeSeries)record;
			omts.checkEndOfUnit(now-_start);
		}
	}
	
	public double getBandwidth(){
		return (((double)(_fileSize*_operations*1000))/((double)_latencies*1024));
	}
	
	public double getAverageLatencyPO(){
		return ((double)_latencies/((double)_operations));
	}
	
	public double getAverageOperationsPS(){
		return (((double)_operations*1000)/(double)_latencies);
	}
	
	public boolean isGetBW(){
		return getBW;
	}
	
	public long getOperations(){
		return _operations;
	}
}
